/**
 * 最终版本
 * 1. defineReative 将一个对象的全部属性都变成 响应式， 将此功能封装为 Observe类（相对于version5）
 * 2. 梳理 Observer、Dep、Watcher的关系
 *      先由Observer 设置属性的getter和setter回调，其中Observer使用Dep管理依赖的列表；
 *      而初始化Wacher时，根据传入的参数立即触发Observer的getter，将Watcher的实例推入Deps主题队列中，
 *      修改Observer时触发setter，进一步触发Dep中装的所有Watcher实例的update方法。
 * 
 *    Observer观察对象 包含/使用 Dep依赖管理器 来 管理Watcher依赖
 */



// 封装 Watcher 类（就是Window.target收集的对象, window.target 从哪里来呢？从watcher中来）
class Watcher {
    constructor(vm,exression,callback){
        this.vm = vm; // vue对象, 也就是defineReact 产生的对象
        this.getter = getParsePathFunc(exression);

        this.callback = callback;

        this.value = this.get(); // 当前的值，立刻调用getter
    }

    get(){
        Dep.target = this; // 先将 要收集的全局变量 存起来， this就是这个watcher实例
        let val = this.getter.call(this.vm, this.vm); // 调用getter，也就是调用 vm.a.b.c之类的getter（vm就是被defineReactive调用的对象），将此watcher实例加入Deps的主题队列
        Dep.target = undefined; // 删除 存在的依赖
        return val;
    }

    update(){
        const oldVal = this.value;
        this.value = this.get(); // 获取新的值
        this.callback.call(this.vm, this.value, oldVal); // 监听的回调
    }
}


class Dep{

    static target = null; // 书中写为window.target 这里写为 Dep.target

    constructor(){
        this.subs = []; //订购的主题（订阅者模式），也就是回调函数 队列
    }

    addSub(sub){ // 这里的sub就是watcher实例
        this.subs.push(sub);
    }

    removeSub(sub){
        if(this.subs.length){
            let deleteIndex = this.subs.findIndex(item=>item==sub);
            if(deleteIndex>-1){
                return this.subs.splice(deleteIndex,1);
            }
        }
    }

    // 获取当前 依赖、回调
    depend(){
        if(Dep.target){
            this.addSub(Dep.target) 
        }
    }

    //  触发此属性的修改 回调队列，调用每一个watcher的回调
    notify(val){
        const subs = this.subs.slice(0);
        for(let i=0;i<subs.length;i++){
            subs[i].update(val);
        }
    }
}

// 通过路径 'a.b.c' 获得 obj.a.b.c 的函数
function getParsePathFunc(path){
    const pathReg = /(\w+\.?\w+)+/;
    if(!pathReg.test(path)){
        console.error('非法路径');
        return
    }

    const arr = path.split('.');
    return function(obj){
        let resultObj = obj;
        for(let pathItem of arr){
            if(resultObj){
                resultObj = resultObj[pathItem];
            }else{
                console.error('路径的属性不存在');
                return
            }
        }
        
        return resultObj;
    }
}


class Observer{
    constructor(valueObj){
        this.value = valueObj;
        if( valueObj instanceof Object&&Object.prototype.toString(valueObj) == '[object Object]' ){
            // 保证是对象
            this.walk(valueObj);
        }
    }

    // 将对象的 每一个属性都设置 getter和setter
    walk(obj){
        for(let key of Object.keys(obj)){
            if(typeof obj[key]=='object'){
                this.walk(obj[key]) //接着向下设置
            }

            this.defineReactive(obj,key,obj[key]);            
        }
    }

    //  把 defineReactive 函数移到类的内部
    defineReactive(obj={}, key='',val){

        const dep = new Dep();
        Object.defineProperty(
            obj,
            key,
            {
                enumerable: true,
                configurable: true,
                get: function(){
                    
                    dep.depend();
                    // @error
                    // Dep.target = undefined; // 这一步不能设置，因为存在obj.a.b时就会存在问题，访问a时候正常 而b的时候就不正常了，因为 Dep.target 被重新赋值了
                    return val;             
                },
                set: function(newVal){
                    if(val==newVal){
                        return;
                    } 
                    val = newVal;               
                    dep.notify();
                }
            }
        )
        
        return obj;
    }
}


const testObj = { 
    name:'123', age:18, 
    inner: { text:'打完等我回家' } 
};

testObj.name = '不知u是';

const observer = new Observer(testObj); 


const watch1 = new Watcher(testObj,'name',function(newVal,oldVal){
    console.log(`ppap name 属性值改变，原本为: ${oldVal}, 新值为: ${newVal}====`);
});


const watch3 = new Watcher(testObj,'inner',function(newVal,oldVal){
    console.log(`pk: inner 属性值改变，原本为: ${oldVal}, 新值为: ${newVal}==`);
});

const watch2 = new Watcher(testObj,'inner.text',function(newVal,oldVal){
    console.log(`ppk: inner.text 属性值改变，原本为: ${oldVal}, 新值为: ${newVal}======`);
});


testObj.name = '不得不结婚后就wqdw';

testObj.inner.text = '的那位考勤机电脑网卡驱动框架 你看11w';

